#ifndef __QLOGGER_H__
#define __QLOGGER_H__

#include <QString>
#include <QMutex>
#include <QHash>
#include <QDateTime>
#include <QTextStream>

class QLogger;
class QLoggerAppender;
class QLoggerEvent;

class QLoggerEngine
{
    private:
        friend class QLoggerAppender;
        QHash<QString, QLogger *> loggers;
        QList<QLoggerAppender *> appenders;
        QTime startTime;

    private:
        static QLoggerEngine &handle()
        {
            static QLoggerEngine hInstance;
            if (hInstance.startTime.isNull())
                hInstance.startTime.start();
            return hInstance;
        }
        ~QLoggerEngine ();

    public:
        typedef enum {
            LevelDebug = 1,
            LevelInfo = 2,
            LevelWarn = 4,
            LevelError = 8,
            LevelCritical = 16,
            LevelFatal = 32,
            LevelException = 64,
            LevelAll = 127
        } QLoggerLevel;

    public:
        int elapsed();

        static void configureConsoleAppender(int level = LevelAll, FILE *stream=stdout, const QString &format=QString());
        static void configureFileAppender(int level = LevelAll, const QString &filename=QString(), const QString &format=QString(), bool truncate=true, bool backup=true);

        static QLogger & logger (const QString &moduleName);
        static void logEvent (const QLogger &logger, QLoggerLevel level, const QString &message, const char *file, int line);
        static bool isEventAccepted (QLoggerAppender *appender, QLoggerEvent &event);

        static const char *levelName(QLoggerLevel level);
};

#define logDebug(logger, message) QLoggerEngine::logEvent(logger, QLoggerEngine::LevelDebug, message, __FILE__, __LINE__)
#define logInfo(logger, message) QLoggerEngine::logEvent(logger, QLoggerEngine::LevelInfo, message, __FILE__, __LINE__)
#define logWarn(logger, message) QLoggerEngine::logEvent(logger, QLoggerEngine::LevelWarn, message, __FILE__, __LINE__)
#define logError(logger, message) QLoggerEngine::logEvent(logger, QLoggerEngine::LevelError, message, __FILE__, __LINE__)
#define logFatal(logger, message) QLoggerEngine::logEvent(logger, QLoggerEngine::LevelFatal, message, __FILE__, __LINE__)
#define logCritical(logger, message) QLoggerEngine::logEvent(logger, QLoggerEngine::LevelCritical, message, __FILE__, __LINE__)
#define logException(logger, message) QLoggerEngine::logEvent(logger, QLoggerEngine::LevelException, message, __FILE__, __LINE__)

class QLogger
{
    private:
        friend class QLoggerEngine;

    public:
        QString name;

    private:
        QLogger(QString moduleName);
};

class QLoggerEvent
{
    public:
        QLoggerEngine::QLoggerLevel level;
        QDateTime dateTime;
        const QLogger *logger;
        const QString *message;
        const char *file;
        int line;
    public:
        QLoggerEvent() : logger(NULL), message(NULL) {}
};

class QLoggerAppender
{
    private:
        QString _format;
        QLoggerEngine::QLoggerLevel _level;

    protected:
        QMutex mutex;

    protected:
        QLoggerAppender (const QString &format, int level);
        void streamWrite (QTextStream &stream, const QLoggerEvent &event);

    public:
        virtual void write (const QLoggerEvent &event) = 0;
        QLoggerEngine::QLoggerLevel level();
};

class QLoggerConsoleAppender : public QLoggerAppender
{
    public:
        FILE *stream;

    public:
        void write (const QLoggerEvent &event);
        QLoggerConsoleAppender (int level, FILE *stream, const QString &format);
};

class QLoggerFileAppender : public QLoggerAppender
{
    private:
        QString _filename;
    public:
        void write (const QLoggerEvent &event);
        QLoggerFileAppender (int level, const QString &filename, const QString &format);
};

#endif
